#ifndef _CGCM_PARAMS_
#define _CGCM_PARAMS_

#include <cstdlib>
#include "datatypes.h"
#include "constants.h"
#include "psystem.h"
#include "ssystem.h"
#include "diagnostics.h"
#include "misclib.h"
#include "readers.h"
#include "qgcmio.h"
#include "preprocessor.h"
#include "compiler.h"
#include "debugger.h"

/****************************************************************************

  class model_class :: defines the model container class, which handles
  all IO, state storage, and other things model related.

 ****************************************************************************/
class model_class {
	friend class psystem;
	friend class ssystem;
	friend class preprocessordata;
	friend class preprocessor;
	friend class compiler;
	friend class debugger;
	friend class model_diagnostics_block; 

	public:
	// contructors
	model_class();                       // contructor that reads namelist
	~model_class();                      // deconstructor

	private:
	struct {
		bool preprocessed;
		bool compiled;
		bool io_initialized;
		bool field_geometry_initialized;
		bool physics_initialized;
		bool state_initialized;
	} model_state;

	struct { 
		string namelist;	
		string stdout_name;
		string stdin_name;
		string stdmsg_name;
	} default_labels;

	psystem pmodule;
	ssystem smodule;
	debugger mdebugger;
	vector<model_diagnostics_block*> mdiags;

	preprocessordata preprocdata;

	public:
	class local; // define local functions

	const string get_default_stdin() const;
	const string get_default_stdout() const;
	const string get_default_stdmsg() const;

	void set_default_namelist(const char *);
	void set_default_stdout(const char *);
	void set_default_stdin(const char *);
	void set_default_stdmsg(const char *); 

	const namelistdata & getnamelistbyid(size_t) const;

	const psystem & getPsystem() const;
	const ssystem & getSsystem() const;             
	psystem & getPsystem();             // returns P-system reference
	ssystem & getSsystem();             // returns S-system reference

	bool runstatusblocks(bool);           // call this function only when user requests status info 
	bool rundiagnosticblocks(bool, bool); // run the diagnostic blocks

	int get_global_param_index_from_name (const char *) const;
	int get_global_prof_index_from_name  (const char *) const;

	private:
	bool init_master_output(const string & stdname, const string & msgname);
	bool init_output(const string & stdname, const string & msgname); 
	bool init_input(const string & );

	public:
	void preprocess();
	void compile();
	int runscript(const vector<const char *> &);
	void init_io();
	void init_field_maps();
	void init_physics();
	void init_state();

	parameter & get_global_param_by_index(const size_t &);
	const parameter & get_global_param_by_index(const size_t &) const;
	const parameter & get_global_param_by_name (const char *) const;
	size_t get_num_global_params() const;
	bool does_param_exist( const char * ) const;

	const parameter & get_global_prof_by_name(const char *) const; // backwards compatibility
	const parameter & get_global_prof_by_index(const size_t &) const;
	parameter & get_global_prof_by_index(const size_t &);
	size_t get_num_global_profs() const;
	bool does_prof_exist(const char *) const;

	void addprofile( parameter * );
	void addparameter( parameter * );

	public : 
	bool isdebuginputvalid() const; // is it a terminal device or pipe ?

	private:
	qstream & getSTDIN();
	qstream & getSTDMSG();
	const qstream & getSTDMSG() const;
	public:
	const qstream & getSTDOUT() const;
	qstream & getSTDOUT();
	qstream & getDBGIN();
	const qstream & getDBGOUT() const;
	private:
	const qstream & getmasterSTDMSG() const;
	qstream & getmasterSTDMSG() ;
	const qstream & getmasterSTDOUT() const;
	qstream & getmasterSTDOUT() ;
	public:

	bool setSTDOUT(const char *);
	bool setSTDMSG(const char *);
	bool setDBGIN(const char *);
	bool setDBGOUT(const char *);
	void closeDBGIO();

	bool readfifo();

	int get_num_threads() const;

	// Static weight calculation functions:

	private:
	vector<rank3> Afieldindices;

	public:
	void setAfieldindices(const vector<rank3> &);

	REAL computeA(const rank6 &) const;
	REAL computeQ(const rank3 &) const;
	REAL computeQI(const rank3 &) const;

	const vector<rank3> & getAfieldindices() const;


	// S-system preprocessor data

	const spreprocdata & getspreprocData() const;
	void clearspreprocdata();

	bool iscompiled() const;
	bool issymboldefined(const char *) const;
	bool undefinemacro(const string &);
	bool definemacro(const string &, const string &);
	bool definemacro(const string &, const mup::token_list &);
	bool addmacrolist(const string &);
	const map< string, token_list > & getmacros() const;

	void readstring(const char *, string &);

	public:
	const string & getnamelistname(const size_t & index) const;
	public:

	bool nc_save_model_state(const char *) const;
	void nc_restore_model_state_from_file(const char *);

	void assignparserparams(ParserX &) const;
	void assignparserprofs(ParserX &) const;
	void assignparserdata(ParserX &, const char * types, bool optimized, bool diag=true, int pindex = -1);

	void setdebugonerror();
	bool debugonerror() const;
	void getdiaglist(vector< const model_diagnostics_block *> & list) const;
	void getfunctiondiaglist(vector< model_diagnostics_block * > & list);
	void getfunctiondiaglist(vector< const model_diagnostics_block * > & list) const;

	void assigndiagfunctions(ParserX &);

	int printf(const char *, ...) const; // print to standard out
	int printfmsg(const char *, ...) const; // print to standard msg

	int flushmessages() const;
	int flushstdout() const;

	private:
	int init_level; // each level corresponds to different levels of initialization
	model_class(const model_class &);
	model_class operator=(const model_class &);

	enum mode_type { none, profs, params };

	// Compilation functions (some implemented as classes so that the call stack can be easily modified in the source only)

	void compilenamelists();

	int  assignparam(int pindex, const token_list & valExp);
	int  assignprof (int pindex, const vector<token_list> & vals, bool, bool);

	vector<parameter *> GLOBAL_PARAMETERS; 
	vector<parameter *> GLOBAL_PROFILES;

	vector< pair<string, vector<string> > > MACROS;


	INDEX atmosphere_layer_1;
	INDEX atmosphere_layer_2;
	INDEX mixed_layer;
	INDEX ocean_layer_1;
	INDEX ocean_layer_2;
	INDEX topo_layer;

	public:
	INDEX get_atmosphere_layer_1_field_number() const;
	INDEX get_atmosphere_layer_2_field_number() const;
	INDEX get_ocean_layer_1_field_number() const;
	INDEX get_ocean_layer_2_field_number() const;
	INDEX get_mixed_layer_field_number() const;
	INDEX get_topo_layer_field_number() const;


	private:

	size_t out_handle_size;
	vector<qstream> MSGOUTs;
	// FILE ** MSGOUTs; // out handles for writing messages and errors
	vector<qstream> STDOUTs; // out handles for writing standard output
	qstream STDIN; // in handle for reading standard input
	// the getSTDIN function will re-open the file at this location each time unless stdin is used
	// for input.				

	spreprocdata spre_data;

	static_container rcoeffs;
	static_container scoeffs;
	map_container<REAL, 6> Acoeffs;
	map_container<REAL, 3> Qcoeffs;
	map_container<REAL, 3> QIcoeffs;

	public:
	void setbreakpoint();
	void removebreakpoint();
	bool isbreakpointon();
	void removeallbreakpoints();
	private:
	bool compiled; // is the namelist and are the diag blocks compiled already ??
};
// ---> end model_class

// Global model_class retriever function ..
model_class & getModel();

// Auxilary function
bool istokendefinedinparser(const ParserX & p, const string & token);
// deprecated
#endif
